package com.solomon.travel.common.util;

import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.MessageDigest;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;

import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

public class SmsUtil {
	private static String url;
	private static String secret;
	private static String appkey;
	public static String appSecret;
	private static final String SIGN_METHOD_HMAC = "hmac";
	private static final String SIGN_METHOD_MD5 = "md5";
	
	static{
		ResourceBundle bundle = ResourceBundle.getBundle("sms");
		if (bundle == null) {
			throw new IllegalArgumentException("[sms.properties] is not found!");
		}
		appkey = bundle.getString("sms.appkey");
		url = bundle.getString("sms.product");
		secret = bundle.getString("sms.appSecret");
		appSecret = bundle.getString("sms.secret");
	}
	
	public static String sendValidateCode(String phoneNumber,String templateCode) throws Exception{
		HashMap<String, Object> params= new HashMap<>();
		String validationCode = randomCode();
		if (isEmpty(templateCode)) {
			templateCode = "SMS_47015132";
		}
		params.put("app_key", appkey);
		params.put("format", "json");
		params.put("method", "alibaba.aliqin.fc.sms.num.send");
		params.put("sign_method", SIGN_METHOD_HMAC);
		SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		params.put("timestamp", format.format(new Date()));
		params.put("v", "2.0");
		params.put("rec_num", phoneNumber);
		params.put("sms_free_sign_name", "家家牛");
		params.put("sms_param", "{code:'" + validationCode + "',product:'家家牛智慧社区'}" );
		params.put("sms_template_code",  templateCode);
		params.put("sms_type", "normal");
		params.put("sign", signTopRequest(params, secret, SIGN_METHOD_HMAC));

		try {
			String response = HttpClientUtil.httpPostRequest(url, params);
			System.out.println(response);
			
			JSONObject jsonObject = JSON.parseObject(response)
					.getJSONObject("alibaba_aliqin_fc_sms_num_send_response")
					.getJSONObject("result");
			Boolean isSuccess = jsonObject.getBoolean("success");
			if (isSuccess) {
				return validationCode;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}
	
	private static String randomCode() {
		String[] beforeShuffle = new String[] { "0","1","2", "3", "4", "5", "6", "7",    
	             "8", "9" };    
	     List<String> list = Arrays.asList(beforeShuffle);    
	     Collections.shuffle(list);    
	     StringBuilder sb = new StringBuilder();    
	     for (int i = 0; i < list.size(); i++) {    
	         sb.append(list.get(i));    
	     }    
	     String afterShuffle = sb.toString();    
	     return afterShuffle.substring(5, 9);
	}
	
	private static byte[] encryptHMAC(String data, String secret) throws IOException {
		byte[] bytes = null;
		try {
			SecretKey secretKey = new SecretKeySpec(secret.getBytes("UTF-8"), "HmacMD5");
			Mac mac = Mac.getInstance(secretKey.getAlgorithm());
			mac.init(secretKey);
			bytes = mac.doFinal(data.getBytes("UTF-8"));
		} catch (GeneralSecurityException gse) {
			throw new IOException(gse.toString());
		}
		return bytes;
	}
	
	private static String signTopRequest(Map<String, Object> params, String secret, String signMethod) throws IOException {
		// 第一步：检查参数是否已经排序
				String[] keys = params.keySet().toArray(new String[0]);
				Arrays.sort(keys);

				// 第二步：把所有参数名和参数值串在一起
				StringBuilder query = new StringBuilder();
				if (SIGN_METHOD_MD5.equals(signMethod)) {
					query.append(secret);
				}
				for (String key : keys) {
					String value = (String) params.get(key);
					if (areNotEmpty(key, value)) {
						query.append(key).append(value);
					}
				}

				// 第三步：使用MD5/HMAC加密
				byte[] bytes;
				if (SIGN_METHOD_HMAC.equals(signMethod)) {
					bytes = encryptHMAC(query.toString(), secret);
				} else {
					query.append(secret);
					bytes = encryptMD5(query.toString());
				}

				// 第四步：把二进制转化为大写的十六进制
				return byte2hex(bytes);
	}

	private static byte[] encryptMD5(String data) throws IOException {
		return encryptMD5(data.getBytes("UTF-8"));
	}

	/**
	 * 对字节流进行MD5摘要。
	 */
	private static byte[] encryptMD5(byte[] data) throws IOException {
		byte[] bytes = null;
		try {
			MessageDigest md = MessageDigest.getInstance("MD5");
			bytes = md.digest(data);
		} catch (GeneralSecurityException gse) {
			throw new IOException(gse.toString());
		}
		return bytes;
	}

	private static  String byte2hex(byte[] bytes) {
		StringBuilder sign = new StringBuilder();
		for (int i = 0; i < bytes.length; i++) {
			String hex = Integer.toHexString(bytes[i] & 0xFF);
			if (hex.length() == 1) {
				sign.append("0");
			}
			sign.append(hex.toUpperCase());
		}
		return sign.toString();
	}

	/**
	 * 检查指定的字符串列表是否不为空。
	 */
	private static  boolean areNotEmpty(String... values) {
		boolean result = true;
		if (values == null || values.length == 0) {
			result = false;
		} else {
			for (String value : values) {
				result &= !isEmpty(value);
			}
		}
		return result;
	}

	/**
	 * 检查指定的字符串是否为空。
	 * <ul>
	 * <li>SysUtils.isEmpty(null) = true</li>
	 * <li>SysUtils.isEmpty("") = true</li>
	 * <li>SysUtils.isEmpty("   ") = true</li>
	 * <li>SysUtils.isEmpty("abc") = false</li>
	 * </ul>
	 *
	 * @param value 待检查的字符串
	 * @return true/false
	 */
	private static boolean isEmpty(String value) {
		int strLen;
		if (value == null || (strLen = value.length()) == 0) {
			return true;
		}
		for (int i = 0; i < strLen; i++) {
			if ((Character.isWhitespace(value.charAt(i)) == false)) {
				return false;
			}
		}
		return true;
	}
}
